Skip to content

fix: distinct signal exit codes, mid-stream API error surfacing, require model name with base URL#54

Merged
min0625 merged 1 commit into
mainfrom
fix/signal-exit-codes-and-stream-errors
Jul 2, 2026
Merged

fix: distinct signal exit codes, mid-stream API error surfacing, require model name with base URL#54
min0625 merged 1 commit into
mainfrom
fix/signal-exit-codes-and-stream-errors

Conversation

@min0625

@min0625 min0625 commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Map SIGINT/SIGTERM to conventional 128+N exit codes (130/143) instead of always returning 130, by tracking the signal via context.WithCancelCause instead of signal.NotifyContext.
  • Surface mid-stream error events/objects from all three providers (Anthropic, OpenAI, Google GenAI) instead of silently returning empty/partial output — streaming responses return HTTP 200 before an error can appear later in the SSE stream.
  • Detect Anthropic max_tokens truncation via stop_reason and return an error instead of printing incomplete output with exit 0.
  • Require MINT_MODEL_NAME when MINT_BASE_URL is set, since a custom endpoint has no meaningful default model (e.g. Ollama would silently get sent a cloud provider's default model name).
  • Add LC_MESSAGES to the locale fallback chain (LC_ALL > LC_MESSAGES > LANG), matching POSIX priority.
  • Strip leading v from the locally-built version string so mint --version matches goreleaser's {{.Version}} for both local and released binaries.
  • Check out the release tag (not main HEAD) in publish-npm.yml/publish-pypi.yml so packaging scripts/README match the release being published.
  • Handle unset $SHELL in install.sh and signal-killed child exit status in the npm wrapper script.
  • Docs (AGENTS.md, all README variants) updated to match.

Test plan

  • make check (lint + go test -race -failfast) passes
  • New unit tests cover: SIGINT vs SIGTERM exit codes, LC_MESSAGES priority, mid-stream error events for all three providers, Anthropic truncation detection, config validation requiring model name with base URL

@qodo-code-review

Copy link
Copy Markdown

Qodo is busy working

Check back in a few minutes. Qodo's code review agents are on it.

Grey Divider

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.93750% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/provider/googlegenai/google_genai.go 76.47% 2 Missing and 2 partials ⚠️
internal/provider/anthropic/anthropic.go 85.71% 1 Missing and 1 partial ⚠️
internal/provider/openai/openai.go 84.61% 1 Missing and 1 partial ⚠️
cmd/mint/main.go 94.44% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix signal exit codes, stream error surfacing, and base URL model validation

🐞 Bug fix 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Map SIGINT/SIGTERM to distinct conventional exit codes (130/143).
• Surface mid-stream SSE errors and Anthropic truncation as non-zero failures.
• Require model name for custom base URLs; align locale/version and release packaging.
Diagram

graph TD
  A["mint CLI"] --> B["Signal-aware context"] --> C["Root command exec"] --> D["Config + locale resolve"] --> E["Provider completer"] --> F{{"LLM SSE API"}}
  E --> G["Stream output + usage"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Centralize SSE decoding/error surfacing in a shared stream layer
  • ➕ Eliminates duplicated per-provider checks for mid-stream error objects/events
  • ➕ Makes future provider additions more consistent and less error-prone
  • ➕ Easier to unit test stream semantics once
  • ➖ Requires designing a common event model across providers with different schemas
  • ➖ Potentially more refactor churn than the current targeted fixes
2. Return partial streamed output but annotate and exit non-zero on truncation/errors
  • ➕ Preserves currently-streamed output for interactive users
  • ➕ Keeps failure semantics explicit for scripting
  • ➖ Still risks consumers treating partial output as valid if they ignore exit codes
  • ➖ Adds UX decisions (where/how to annotate) that vary by output mode
3. Keep signal.NotifyContext and separately track last signal via Notify channel
  • ➕ Less custom context wiring in main()
  • ➕ Retains standard library convenience
  • ➖ Racy/ambiguous if multiple signals arrive or context cancels for other reasons
  • ➖ Harder to guarantee the exit code matches the actual cancel cause

Recommendation: The PR’s approach is a good pragmatic balance: it fixes correctness issues (exit codes and silent stream failures) with minimal surface-area changes to each provider. If streaming logic grows further, consider consolidating SSE parsing/error extraction into a shared component to reduce duplication and ensure consistent semantics.

Files changed (19) +299 / -46

Bug fix (7) +118 / -15
main.goTrack signal cancel causes for correct exit codes; add LC_MESSAGES fallback +41/-8

Track signal cancel causes for correct exit codes; add LC_MESSAGES fallback

• Replaces signal.NotifyContext with context.WithCancelCause plus a signal channel to preserve which signal triggered cancellation, enabling conventional 128+N exit codes. Updates locale detection to follow LC_ALL > LC_MESSAGES > LANG.

cmd/mint/main.go

anthropic.goSurface Anthropic stream errors and max_tokens truncation +34/-4

Surface Anthropic stream errors and max_tokens truncation

• Parses mid-stream error events and returns them as errors even when HTTP status is 200. Detects output truncation via stop_reason=max_tokens and returns an explicit truncation error after streaming.

internal/provider/anthropic/anthropic.go

config.goRequire model name when a custom base URL is set +7/-0

Require model name when a custom base URL is set

• Adds validation rejecting configs that set MINT_BASE_URL without also providing MINT_MODEL_NAME to avoid meaningless provider defaults against custom endpoints.

internal/provider/config.go

google_genai.goTreat Google GenAI stream error objects as failures +13/-0

Treat Google GenAI stream error objects as failures

• Extends stream response parsing to detect an embedded error object and return it as an error, addressing cases where HTTP status is 200 but the stream reports failure later.

internal/provider/googlegenai/google_genai.go

openai.goTreat OpenAI-compatible stream error objects as failures +13/-0

Treat OpenAI-compatible stream error objects as failures

• Adds support for detecting a mid-stream error object in the SSE payload and returning an error instead of silently producing empty/partial output.

internal/provider/openai/openai.go

mintPropagate signal-killed child processes as 128+N exit codes +6/-0

Propagate signal-killed child processes as 128+N exit codes

• Detects when the wrapped binary exited due to a signal and exits with the conventional 128+N code instead of masking it as success (status null).

npm/mint-ai/scripts/mint

install.shHandle unset $SHELL under set -u during install +4/-3

Handle unset $SHELL under set -u during install

• Uses ${SHELL:-} in shell detection so the installer doesn't abort in minimal environments where SHELL is unset.

script/install.sh

Tests (5) +161 / -25
main_test.goAdd tests for LC_MESSAGES priority and distinct signal exit codes +33/-24

Add tests for LC_MESSAGES priority and distinct signal exit codes

• Extends getSystemLanguage coverage to validate LC_MESSAGES precedence and fallback behavior. Updates run() interruption test to assert SIGINT=130 and SIGTERM=143.

cmd/mint/main_test.go

anthropic_test.goTest Anthropic truncation detection and mid-stream error events +65/-0

Test Anthropic truncation detection and mid-stream error events

• Adds SSE-based tests ensuring max_tokens truncation becomes an error while still delivering already-streamed output and usage. Adds coverage for error events arriving mid-stream.

internal/provider/anthropic/anthropic_test.go

config_test.goAdd config validation tests for base URL + model name requirement +11/-1

Add config validation tests for base URL + model name requirement

• Updates existing base-URL test cases to include an explicit model. Adds new cases ensuring missing model name is rejected even when an API key is present.

internal/provider/config_test.go

google_genai_test.goTest Google GenAI mid-stream error object propagation +25/-0

Test Google GenAI mid-stream error object propagation

• Adds an SSE-based unit test asserting that a chunk containing an error object results in a non-nil error with details.

internal/provider/googlegenai/google_genai_test.go

openai_test.goTest OpenAI mid-stream error object propagation +27/-0

Test OpenAI mid-stream error object propagation

• Adds an SSE-based unit test ensuring a stream error object produces a non-nil error containing the server-provided message.

internal/provider/openai/openai_test.go

Documentation (4) +6 / -4
AGENTS.mdDocument LC_MESSAGES locale priority and internal modules +3/-1

Document LC_MESSAGES locale priority and internal modules

• Updates the documented target-language fallback chain to include LC_MESSAGES per POSIX. Also refreshes internal file pointers referenced by the agent docs.

AGENTS.md

README.ja.mdClarify model-name requirement when using custom base URL +1/-1

Clarify model-name requirement when using custom base URL

• Updates the environment variable table to state MINT_MODEL_NAME is required when MINT_BASE_URL is set.

README.ja.md

README.mdClarify model-name requirement when using custom base URL +1/-1

Clarify model-name requirement when using custom base URL

• Updates the environment variable table to state MINT_MODEL_NAME is required when MINT_BASE_URL is set.

README.md

README.zh-TW.mdClarify model-name requirement when using custom base URL +1/-1

Clarify model-name requirement when using custom base URL

• Updates the environment variable table to state MINT_MODEL_NAME is required when MINT_BASE_URL is set.

README.zh-TW.md

Other (3) +14 / -2
publish-npm.ymlCheckout release tag when publishing npm package +5/-0

Checkout release tag when publishing npm package

• Pins actions/checkout to the workflow_run tag ref so packaging scripts/README match the published release rather than main HEAD.

.github/workflows/publish-npm.yml

publish-pypi.ymlCheckout release tag and fix PyPI publish input name +6/-1

Checkout release tag and fix PyPI publish input name

• Checks out the release tag ref for publishing parity with the release content. Renames the PyPI publish input from skip_existing to the correct skip-existing key.

.github/workflows/publish-pypi.yml

MakefileNormalize local VERSION string by stripping leading 'v' +3/-1

Normalize local VERSION string by stripping leading 'v'

• Strips a leading 'v' from git tag-derived versions so local builds match goreleaser's {{.Version}} output.

Makefile

@min0625 min0625 force-pushed the fix/signal-exit-codes-and-stream-errors branch from e59623e to 0564c0f Compare July 2, 2026 07:34
…s, require model name with base URL

- Map SIGINT/SIGTERM to conventional 128+N exit codes (130/143) instead of always 130
- Surface mid-stream error events/objects from all three providers instead of silently returning empty/partial output
- Detect Anthropic max_tokens truncation and return an error instead of printing incomplete output with exit 0
- Require MINT_MODEL_NAME when MINT_BASE_URL is set, since a custom endpoint has no meaningful default model
- Add LC_MESSAGES to the locale fallback chain (LC_ALL > LC_MESSAGES > LANG)
- Strip leading "v" from local build version so it matches goreleaser's version string
- Check out the release tag (not main) in publish-npm/publish-pypi workflows
- Handle unset $SHELL and killed-by-signal exit status in install.sh / npm wrapper script
@min0625 min0625 force-pushed the fix/signal-exit-codes-and-stream-errors branch from 0564c0f to b5eba79 Compare July 2, 2026 11:56
@min0625 min0625 merged commit de2ac90 into main Jul 2, 2026
6 checks passed
@min0625 min0625 deleted the fix/signal-exit-codes-and-stream-errors branch July 2, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant